/*
 * Copyright (c) 2019-2022. Alexey Antipin and others.
 * https://github.com/Evreke/warships
 *
 * Licensed under the Apache License 2.0
 */


class Game(
  val id: String,
  private var initialPlayer: ActualPlayer,
) {
  private val playerToBoard = mutableMapOf<String, Board>() // TODO Players boards state stored here
  val players = linkedSetOf<ActualPlayer>()
  private val isStarted: Boolean = false
  private var move: Int = 0
  private val isDebug: Boolean = true
  private var currentPlayerId: String
  // TODO Store the ID of the current player to be able to retrieve player and opponent boards.
  //  This will allow not to pass the ID of the current player in the necessary methods

  /**
   * Init game with one player.
   */
  init {
    if (isDebug) {
      joinPlayer(
        initialPlayer,
        initBoard().also { it.placeShipsForDemo() }
      )
    } else {
      joinPlayer(
        initialPlayer,
        initBoard()
      )
    }
    currentPlayerId = initialPlayer.playerId
  }

  companion object {
    private const val boardSize = 10
  }

  private fun incrementMove() {
    move++
  }

  /**
   * Get current players board or current players and opponents with masked ships
   */
  fun getUpdatedBoards(playerId: String): Map<String, Board> {
    return when (players.size) {
      1 -> mapOf(playerId to getBoardByPlayerId(playerId))


      2 -> mapOf(
        playerId to getBoardByPlayerId(playerId),
        players.first { it.playerId != playerId }.let { it.playerId to getBoardByPlayerId(it.playerId).maskBoard() }
      )

      else -> error("No players")
    }
  }

  fun joinPlayer(player: ActualPlayer, board: Board) {
    // TODO check if player can join
    players.add(player)
    playerToBoard[player.playerId] = board
  }

  fun isGameOver() {
    if (initialPlayer.shipsAmount == 0.toByte()) {
      // TODO("Send game over signal")
    }
    // TODO("Handle game over")
  }

  private fun switchPlayers() {
    currentPlayerId = players.first { it.playerId != currentPlayerId }.playerId
  }

  fun handleShot(coordinates: Coordinates) {
    val opponentBoard = getOpponentBoard()
    if (isShotPossible(coordinates, opponentBoard)) {
      opponentBoard.field[coordinates.y][coordinates.x] = FieldConstants.WRECKED
    } else {
      opponentBoard.field[coordinates.y][coordinates.x] = FieldConstants.SHOT
    }
  }

  fun getBoardByPlayerId(playerId: String): Board =
    playerToBoard[playerId] ?: error("No game board for player $currentPlayerId")

  private fun getPlayerBoard(): Board =
    playerToBoard[currentPlayerId] ?: error("No game board for player $currentPlayerId")

  private fun getOpponentBoard(): Board =
    playerToBoard.entries.firstOrNull { it.key != currentPlayerId }?.value ?: error("No game board for opponent")

  private fun isShotPossible(coordinates: Coordinates, board: Board): Boolean =
    board.field[coordinates.y][coordinates.x]
      .let { it != FieldConstants.SHOT && it != FieldConstants.EMPTY && it != FieldConstants.WRECKED }

  fun initBoard(): Board {
    val emptyField = mutableListOf<MutableList<Int>>().also { list ->
      repeat(boardSize) {
        list.add(MutableList(boardSize) { FieldConstants.EMPTY })
      }
    }

    return Board(emptyField, boardSize)
  }
}
